//== SAGITTARIUS ============================================================================

//== FILEDOC =========================================================================

/** @file lexcore.h
  * @brief Just what it says: the kernel of the lexer
  * @details Gets included in every other source file that interfaces with or is
  * part of the lexer
  */

/**
 * @def LX_ALLOC
 * @brief Performs allocation within the lexer
 * @details Wraps to either malloc or a memory pool to do allocation.
 * The memory pool can simply be discarded after the DFA is generated.
 */

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGUTTARUIS__LEXCORE__H_
#define __SAGUTTARUIS__LEXCORE__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/hash/hash.h"
#if SAGITTARIUS_LXALPHA_USE_GMP
    #include "sagittarius/gmp/gmpsupport.h"
    #include "sagittarius/gmp/gmpsupport.h"
    #include "sagittarius/unicode/GMPUnicode.h"
#else
    #include "sagittarius/unicode/CUnicode.h"
    #include <malloc.h>
#endif

#define LX_USE_POOLALLOC SAGITTARIUS_LX_USE_POOLALLOC
#if LX_USE_POOLALLOC
    #include "sagittarius/mem/lsegalloc.h"
    #define LX_ALLOC(bytes) lx_alloc(bytes)
    #define LX_FREE(bytes)
#else
    #include <malloc.h>
    #define LX_ALLOC(bytes) malloc(bytes)
    #define LX_FREE(bytes) free(bytes)
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if !SAGITTARIUS_LXALPHA_USE_GMP
    ///@brief The basic type of symbol understood by the lexer
    typedef UnicodeCChar LXAlphaType;
    /** @brief Always typedef'd to a machine word, even in bignum mode
     * @details Defaults to @ref LXAlphaType in fixed mode
     */
    typedef UnicodeCChar LXFixedType;
    ///@brief const cast of @ref LXAlphaType where possible
    typedef const UnicodeCChar LXConstAlphaType;
    ///@brief A typical buffer structure used by the implementation @details Usually just a heap array
    typedef LXAlphaType* LexBuf;
    ///@brief const cast of @ref LexBuf where possible
    typedef const LXAlphaType* constLexBuf;

    #define LX_ALPHA_SET(x, val)       x = val
    #define LX_ALPHA_SET_SPC(x, val)   x = val
    #define LX_ALPHA_CLEAR(x)          //no op
    #define LX_FORCE_INIT(x)
    #define LX_INIT_SET(x, y) x = y
    #define LX_INIT_SET_SPC(x, y) x = y

    #define LX_CMPALPHA_EQ(x, y) (x == y)
    #define LX_CMPALPHA_EQ_SPC(x, y) (x == y) //special: y is usually an unsigned long or something
    #define LX_CMPALPHA_LT(x, y) (x < y)
    #define LX_CMPALPHA_LTE(x, y) (x <= y)
    #define LX_CMPALPHA_GT(x, y) (x > y)
    #define LX_CMPALPHA_GTE(x, y) (x >= y)
    #define LX_CMPALPHA_GTE_SPC(x, y) (x >= y)

    //arithmetic
    #define LX_ALPHA_INC(x)          ++x //don't rely on the result of this expression
    #define LX_ALPHA_DEC(x)          --x
    #define LX_ALPHA_ADD(r,x,y)      r = x+y
    #define LX_ALPHA_ADD_SPC(r,x,y)  r = x+y
    #define LX_ALPHA_SUB(r,x,y)      r = x-y
    #define LX_ALPHA_LSHIFT(r,a)     (r <<= a)
    #define LX_ALPHA_LSHIFT_SPC(r,x) (r <<= x)
    #define LX_ALPHA_RSHIFT(r,a)     (r >>= a)
    #define LX_ALPHA_RSHIFT_SPC(r,x) (r >>= x)
    
    //#define LX_FIXED_VALUE(x)       x
    #define LX_COPY(dest, src, n)   memcpy(dest, src, (n)*sizeof(LXAlphaType))
    #define LX_CREATE_BUFFER(n)     malloc((n)*sizeof(LXAlphaType))
    #define LX_CREATE_POOL_BUF(n)   LX_ALLOC((n)*sizeof(LXAlphaType))
    //#define LX_CONCAT_BUFFER(p,q)   sg_lex_concatbuf(p, q)
    #define LX_BUFFER_LEN(buf)      UCBufferLen(buf)
    ///Get the hypothetical length of the encoded UTF-8 version of the buf
    #define LX_ENCBUFFER_LEN(buf)   UC_UTF8BufferLen(buf)
    ///Encode the buffer into UTF-8
    #define LX_BUFFER_ENC_UTF8(buf,str) UCEncode(buf, str)
    #define LX_FREE_BUFFER(buf)         free(buf) //FIXME: should this be LX_FREE?
    #define LX_FREE_POOL_BUFFER(buf)
    #define LX_FREE_BUFFERL(buf, n)     free(buf) //FIXME: should this be LX_FREE?
    
    //diag
    #define LX_ALPHA_WRITEINT(fd, x)    sg_writeUInt(fd, x);
    #define LX_WRITE_BUF(fd, buf)       sg_writeUStr(fd, buf)
    #define LX_WRITE_BUFL(fd, buf, len) sg_writeUStrl(fd, buf, len)
    #define LX_GET_FIXED_VALUE(x)   x
    #define LX_IS_NULL(x)   x == 0

#else
    ///*** GMP ***///
    //Enable for GMP:
    #define LX_ALPHA_DO_PERVALUE
    
    typedef bignum_t LXAlphaType;
    typedef unsigned long LXFixedType;
    typedef bignum_t LXConstAlphaType;
    typedef LXAlphaType* LexBuf;
    typedef LXAlphaType* constLexBuf;

    #define LX_ALPHA_SET(x, y)         sg_bignum_set(&(x), &(y))
    #define LX_ALPHA_SET_SPC(x, val)   sg_bignum_set_ui(&(x), val)
    #define LX_ALPHA_CLEAR(x)          sg_bignum_clear(&(x))
    #define LX_FORCE_INIT(x)           (x).flg = 1; mpz_init((x).v)
    #define LX_INIT_SET(x, y)          (x).flg = 1; mpz_init_set((x).v, (y).v)
    #define LX_INIT_SET_SPC(x, y)      (x).flg = 1; mpz_init_set_ui((x).v, y)

    //comparison
    #define LX_CMPALPHA_EQ(x, y)      (SG_BIGNUM_CMP(x, y) == 0)
    #define LX_CMPALPHA_EQ_SPC(x, y)  (SG_BIGNUM_CMP_UI(x, y) == 0) //special: y is usually an unsigned long or something
    #define LX_CMPALPHA_LT(x, y)      (SG_BIGNUM_CMP(x, y) < 0)
    #define LX_CMPALPHA_LTE(x, y)     (SG_BIGNUM_CMP(x, y) <= 0)
    #define LX_CMPALPHA_GT(x, y)      (SG_BIGNUM_CMP(x, y) > 0)
    #define LX_CMPALPHA_GTE(x, y)     (SG_BIGNUM_CMP(x, y) >= 0)
    #define LX_CMPALPHA_GTE_SPC(x, y) (SG_BIGNUM_CMP_UI(x, y) >= 0)

    //arithmetic
    #define LX_ALPHA_INC(x)          mpz_add_ui((x).v, (x).v, 1)
    #define LX_ALPHA_DEC(x)          mpz_sub_ui((x).v, (x).v, 1)
    #define LX_ALPHA_ADD(r,x,y)      mpz_add((r).v,(x).v,(y).v)
    #define LX_ALPHA_ADD_SPC(r,x,y)  mpz_add_ui((r).v,(x).v,y) //y is unsigned long
    #define LX_ALPHA_SUB(r,x,y)      mpz_sub((r).v,(x).v,(y).v)
    #define LX_ALPHA_LSHIFT(r,a)     sg_bignum_lshift(&(r),a)
    #define LX_ALPHA_LSHIFT_SPC(r,x) sg_bignum_lshift_ui(&(r),x)
    #define LX_ALPHA_RSHIFT(r,a)     sg_bignum_rshift(&(r),a)
    #define LX_ALPHA_RSHIFT_SPC(r,x) sg_bignum_rshift_ui(&(r),x)
    
    //#define LX_FIXED_VALUE(x)       sg_make_bignum(x)
    #define LX_COPY(dest, src, n)        gmpcopy(dest, src, n)
    #define LX_CREATE_BUFFER(n)          sg_bignum_create_buf(n)
    #define LX_BUFFER_LEN(buf)           UCGMPBufferLen(buf)
    #define LX_ENCBUFFER_LEN(buf)        UCGMP_UTF8BufferLen(buf)
    #define LX_CREATE_POOL_BUF(n)        LX_CREATE_BUFFER(n)
    //#define LX_CONCAT_BUFFER(p,q)        sg_lex_concatbuf(p, q)
    #define LX_BUFFER_ENC_UTF8(buf,str)  UCGMPEncode_UTF8(buf,str)
    #define LX_FREE_BUFFER(buf)          sg_bignum_deletebuf(buf, UCGMPBufferLen(buf))
    #define LX_FREE_POOL_BUFFER(buf)     sg_bignum_deletebuf(buf, UCGMPBufferLen(buf))
    #define LX_FREE_BUFFERL(buf, n)      sg_bignum_deletebuf(buf, n)
    
    //diag
    #define LX_ALPHA_WRITEINT(fd, x)    sg_bignum_print(x);
    #define LX_WRITE_BUF(fd, buf)   sg_writeGMPUStr(fd, buf)
    #define LX_WRITE_BUFL(fd, buf, len) sg_writeGMPUStrl(fd, buf, len)
    #define LX_GET_FIXED_VALUE(x)   SG_GET_BIGNUM_UI(x)
    #define LX_IS_NULL(x)   LX_CMPALPHA_EQ_SPC(x, 0)
    
#endif

//!! TWO STATEMENTS!
#define LX_SET_RANGE(r, val) LX_ALPHA_SET(r.l, val.l); LX_ALPHA_SET(r.u, val.u)
#define LX_FORCE_INIT_RANGE(r) LX_FORCE_INIT((r).l); LX_FORCE_INIT((r).u)
#define LX_CLEAR_RANGE(r) LX_ALPHA_CLEAR((r).l); LX_ALPHA_CLEAR((r).u)

/** @brief Concatenate two lex strings
 */
//LexBuf sg_lex_concatbuf(constLexBuf p, constLexBuf q);

/** @brief Concatenate a string with a symbol
 * @details Creates new buffer to store the result
 */
LexBuf sg_lex_pushsym(constLexBuf p, LXConstAlphaType x);

/** @brief Copy a buffer
 */
LexBuf sg_lex_copybuf(constLexBuf p);

/** @brief Copy a pool buffer
 */
LexBuf sg_lex_copypbuf(constLexBuf p);

/** @brief Copy a buffer with length
 */
LexBuf sg_lex_copybufl(constLexBuf p, size_t l);

/** @brief Concatenate two buffers
 */
LexBuf sg_lex_concatbuf(constLexBuf p, constLexBuf q);

/** @brief Determine if two buffer are equal
 */
int sg_lex_bufeq(constLexBuf p, constLexBuf q);

/** @brief Determine if two buffer are equal with length param
 */
int sg_lex_bufeql(constLexBuf p, constLexBuf q, size_t l);

/** @brief Concatenate two buffers
 */
LexBuf sg_lex_str2buf(const char* str);

/** @brief Concatenate two buffers
 */
char* sg_lex_buf2str(constLexBuf b);

/** @brief Hash the buffer
 */
uint64 sg_lex_hashbuf(constLexBuf b);

/** @brief Hash the buffer with length
 * @details This is why overloading is a good thing
 */
uint64 sg_lex_hashbufl(constLexBuf b, size_t l);

#ifdef __cplusplus
}//extern "C"
#endif

#ifdef __cplusplus
std::string sg_lex_buf2stdstrl(constLexBuf b, uint64 l);
#endif

#endif
